package com.hice.demo.sbdbra.common.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;

@Component
public class NumberConverter implements GenericConverter {
    private static Logger log = LoggerFactory.getLogger(NumberConverter.class);

    public Set<ConvertiblePair> getConvertibleTypes() {
        ConvertiblePair pair = new ConvertiblePair(String.class, Number.class);
        Set set = new HashSet();
        set.add(pair);

        return set;
    }

    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        Class targetClass = targetType.getType();
        if ((source == null) || (source.toString().equals(""))) {
            source = "0";
        }
        String trimmed = StringUtils.trimAllWhitespace(source.toString());

        if ((Byte.class == targetClass) || (Integer.TYPE == targetClass))
            return isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed);
        if ((Short.class == targetClass) || (Short.TYPE == targetClass))
            return isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed);
        if ((Integer.class == targetClass) || (Integer.TYPE == targetClass))
            return isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed);
        if ((Long.class == targetClass) || (Long.TYPE == targetClass))
            return isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed);
        if (BigInteger.class == targetClass)
            return isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed);
        if ((Float.class == targetClass) || (Float.TYPE == targetClass))
            return Float.valueOf(trimmed);
        if ((Double.class == targetClass) || (Double.TYPE == targetClass))
            return Double.valueOf(trimmed);
        if ((BigDecimal.class == targetClass) || (Number.class == targetClass)) {
            return new BigDecimal(trimmed);
        }
        log.error("Cannot convert String [" + source.toString() + "] to target class [" + targetClass.getName() + "]");

        return null;
    }

    private static boolean isHexNumber(String value) {
        int index = value.startsWith("-") ? 1 : 0;
        return (value.startsWith("0x", index)) || (value.startsWith("0X", index)) || (value.startsWith("#", index));
    }

    private static BigInteger decodeBigInteger(String value) {
        int radix = 10;
        int index = 0;
        boolean negative = false;

        if (value.startsWith("-")) {
            negative = true;
            index++;
        }

        if ((value.startsWith("0x", index)) || (value.startsWith("0X", index))) {
            index += 2;
            radix = 16;
        } else if (value.startsWith("#", index)) {
            index++;
            radix = 16;
        } else if ((value.startsWith("0", index)) && (value.length() > 1 + index)) {
            index++;
            radix = 8;
        }

        BigInteger result = new BigInteger(value.substring(index), radix);
        return negative ? result.negate() : result;
    }
}